package com.spa.infrastructure.scheduled;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spa.application.service.member.MemberOrderApplicationService;
import com.spa.domain.entity.*;
import com.spa.domain.service.*;
import com.spa.infrastructure.common.KeyConstant;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderSubStatusEnum;
import com.spa.infrastructure.repository.condition.CitiesServedCondition;
import com.spa.infrastructure.repository.condition.CouponExchangeCondition;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.repository.po.DouyinOauthPO;
import com.spa.infrastructure.repository.service.SysConfigurationRepositoryService;
import com.spa.infrastructure.service.HttpService;
import com.spa.infrastructure.service.douyin.DouYinWebService;
import com.spa.infrastructure.service.douyin.result.web.OauthReNewRefreshAccessTokenResult;
import com.spa.infrastructure.service.sms.ApplicationConfiguration;
import com.spa.infrastructure.service.sms.SmsProvider;
import com.spa.infrastructure.service.sms.huaxin.HuaXinUtil;
import com.spa.infrastructure.service.yunxin.VoiceSenderService;
import com.spa.infrastructure.util.EnvUtil;
import com.spa.infrastructure.util.OrderUtils;
import com.spa.infrastructure.util.RedisKeyUtil;
import com.spa.infrastructure.util.RedisUtil;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public class Task {

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private UserTicketService userTicketService;

    @Autowired
    private EnvUtil envUtil;

    @Resource
    private HttpService httpService;

    @Resource
    private ShopService shopService;

    @Resource
    private CitiesServedService citiesServedService;

    @Resource
    public ObjectMapper mapper;

    @Resource
    private MemberOrderApplicationService memberOrderApplicationService;

    @Resource
    private ApplicationConfiguration smsConfig;

    @Resource
    private VoiceSenderService voiceSenderService;

    @Resource
    private ShopUserService shopUserService;

    @Resource
    private CouponExchangeService couponExchangeService;

    @Resource
    private CouponService couponService;

    @Resource
    private ProfitService profitService;

    @Resource
    private SmsProvider smsProvider;

    @Resource
    private DouYinOauthService douYinOauthService;

    @Resource
    private DouYinWebService douYinWebService;

    @Autowired
    private SysConfigurationRepositoryService sysConfigurationRepositoryService;

    /**
     * 每个小时执行一次，刷新refreshToken (过期时间提前两个小时)
     */
    @Scheduled(cron = "0 0 */1 * * ?")
    public void refreshToken() throws Exception {
        int current = 1;
        int size = 100;
        Page<DouYinOauth> page = douYinOauthService.page(current, size);
        for (DouYinOauth yinOauth : page.getRecords()) {
            if (yinOauth.getRefreshExpiresDate().before(new Date())) {
                OauthReNewRefreshAccessTokenResult oauthReNewRefreshAccessTokenResult = douYinWebService.renewRefreshAccessToken(yinOauth.getRefreshToken());
                if (oauthReNewRefreshAccessTokenResult.getData().getError_code() == 0) {
                    DouyinOauthPO po = new DouyinOauthPO();
                    String refreshToken = oauthReNewRefreshAccessTokenResult.getData().getRefresh_token();
                    po.setId(yinOauth.getId());
                    po.setRefreshToken(refreshToken);
                    po.setRefreshExpiresDate(OrderUtils.toDate(LocalDateTime.now().plusSeconds(oauthReNewRefreshAccessTokenResult.getData().getExpires_in() - 7200)));
                    douYinOauthService.updateDouYinAccountById(po);
                }
            }
        }
        while (page.hasNext()) {
            current++;
            page = douYinOauthService.page(current, size);
            for (DouYinOauth yinOauth : page.getRecords()) {
                if (yinOauth.getRefreshExpiresDate().before(new Date())) {
                    OauthReNewRefreshAccessTokenResult oauthReNewRefreshAccessTokenResult = douYinWebService.renewRefreshAccessToken(yinOauth.getRefreshToken());
                    if (oauthReNewRefreshAccessTokenResult.getData().getError_code() == 0) {
                        DouyinOauthPO po = new DouyinOauthPO();
                        String refreshToken = oauthReNewRefreshAccessTokenResult.getData().getRefresh_token();
                        po.setId(yinOauth.getId());
                        po.setRefreshToken(refreshToken);
                        po.setRefreshExpiresDate(OrderUtils.toDate(LocalDateTime.now().plusSeconds(oauthReNewRefreshAccessTokenResult.getData().getExpires_in() - 7200)));
                        douYinOauthService.updateDouYinAccountById(po);
                    }
                }
            }
        }
    }

    /**
     * 团购情况
     */
    @Scheduled(cron = "0 1 0 * * ?")
    public void statisticsPerDay4Coupon() throws Exception {
        LocalDate localDate = LocalDate.now().minusDays(1);
        String beginDate = localDate + " 00:00:00";
        String endDate = localDate + " 23:59:59";
        CouponExchangeCondition condition = new CouponExchangeCondition();
        condition.setCreateBeginTime(beginDate);
        condition.setCreateEndTime(endDate);
        long exchangeCount = couponExchangeService.countByCondition(condition);
        long useCount = couponService.countPerDay(beginDate, endDate);
        String content = "# " + localDate + "（全天）\n 抖音团购券平台兑换数量为：<font color=\"warning\">" + exchangeCount + "</font>\n 平台下单使用数量为：<font color=\"warning\">" + useCount +"</font>";
        MarkdownParam markdown = MarkdownParam.builder()
                .msgtype("markdown")
                .markdown(Markdown.builder().content(content).build())
                .build();
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.set("content-type", "application/json");
        String res = httpService.sendPost("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=e8a2d27f-5488-44ee-87f9-a27bd1744fcd", requestHeaders,mapper.writeValueAsString(markdown));
        log.info("{}", res);
    }

    /**
     * <a href="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=e8a2d27f-5488-44ee-87f9-a27bd1744fcd">...</a>
     */
    @Scheduled(cron = "0 1 0 * * ?")
    public void statisticsPerDay() throws Exception {
        LocalDate localDate = LocalDate.now().minusDays(1);
        String beginDate = localDate + " 00:00:00";
        String endDate = localDate + " 23:59:59";
        log.info("{} {}", beginDate, endDate);
        CitiesServedCondition citiesServedCondition = new CitiesServedCondition();
        citiesServedCondition.setStatus(10);
        List<CitiesServed> citiesServedList = citiesServedService.listInfo(citiesServedCondition);
        StringBuilder sb = new StringBuilder();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setBeginDate(beginDate);
        condition.setEndDate(endDate);
        List<OrderInfo> orderInfoList = orderService.orderList(condition);
        int todayPrice = 0;
        int extTotalPrice = 0;
        if (CollUtil.isNotEmpty(orderInfoList)) {
            todayPrice = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getOrderPrice(), 0)).sum();
            extTotalPrice = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getExtTotalPrice(), 0)).sum();
        }
        long ready = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==40).count();
        long ing = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==30).count();
        long cancel$10 = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==10).count();
        long cancel$20 = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==20).count();
        long cancel$30 = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==30).count();
        int extraTimePeoples = orderInfoList.stream().filter(orderInfo -> orderInfo.getOrderExtList() != null)
                .map(orderInfo -> {
                    AtomicInteger total = new AtomicInteger();
                    orderInfo.getOrderExtList().forEach(orderExt -> total.addAndGet(orderExt.getOrderCount()));
                    return total.get();
                }).reduce(Integer::sum).orElse(0);
        //分润
        List<Profit> profits = profitService.queryProfit(beginDate, endDate);
        int income = 0;
        int shopIncome = 0;
        int massagitsIncome = 0;
        int travel = 0;
        if (CollUtil.isNotEmpty(profits)) {
            income = profits.stream().mapToInt(profit -> ObjectUtil.defaultIfNull(profit.getAdminIncome(), 0)).sum();
            shopIncome = profits.stream().mapToInt(profit -> ObjectUtil.defaultIfNull(profit.getShopIncome(), 0)).sum();
            massagitsIncome = profits.stream().mapToInt(profit -> ObjectUtil.defaultIfNull(profit.getMassagistIncome(), 0)).sum();
            travel = profits.stream().mapToInt(profit -> ObjectUtil.defaultIfNull(profit.getTravelPrice(), 0)).sum();
        }
        String profitContent = "<<<<font color=\"info\">平台收入</font>：<font color=\"warning\">" + income/100 + "</font>；门店收入：<font color=\"warning\">" + shopIncome/100 + "</font>；技师收入（包含车费在内）：<font color=\"warning\">" + (massagitsIncome + travel)/100 +"</font>>>>";
        String msg = "# " + localDate + "（按下单时间统计）\n <font color=\"warning\">平台数据</font>---业绩：<font color=\"info\">" + (todayPrice + extTotalPrice)/100 + "</font>元，已完成：<font color=\"info\">" + ready + "</font>单，进行中：<font color=\"info\">" + ing + "</font>单，未支付取消：<font color=\"info\">" + cancel$10 + "</font>单，支付取消：<font color=\"info\">" + cancel$20 + "</font>单，超时自动取消：<font color=\"info\">" + cancel$30 + "</font>单，加钟数：<font color=\"info\">" + extraTimePeoples + "</font>单。";
        sb.append(msg);
        sb.append("\n");
        sb.append("\n");
        sb.append(profitContent);
        sb.append("\n");
        sb.append("\n");
        citiesServedList.forEach(citiesServed -> {
            List<Shop> shop = shopService.getShopByCitiesId(citiesServed.getId());
            OrderInfoCondition condition0 = new OrderInfoCondition();
            condition0.setBeginDate(beginDate);
            condition0.setEndDate(endDate);
            if (CollUtil.isNotEmpty(shop)) {
                condition0.setShopIds(shop.stream().map(Shop::getId).toList());
            }
            List<OrderInfo> orderInfoList0 = orderService.orderList(condition0);
            int todayPrice0 = 0;
            int extTotalPrice0 = 0;
            if (CollUtil.isNotEmpty(orderInfoList0)) {
                todayPrice0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getOrderPrice(), 0)).sum();
                extTotalPrice0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getExtTotalPrice(), 0)).sum();
            }
            long ready0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==40).count();
            long ing0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==30).count();
            long cancel0$10 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==10).count();
            long cancel0$20 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==20).count();
            long cancel0$30 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==99 && orderInfo.getSubStatus()==30).count();
            int extraTimePeoples0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getOrderExtList() != null)
                    .map(orderInfo -> {
                        AtomicInteger total = new AtomicInteger();
                        orderInfo.getOrderExtList().forEach(orderExt -> total.addAndGet(orderExt.getOrderCount()));
                        return total.get();
                    }).reduce(Integer::sum).orElse(0);
            String msg0 = " <font color=\"warning\">" + citiesServed.getCityName() + "</font>数据---业绩：<font color=\"info\">" + (todayPrice0 + extTotalPrice0)/100 + "</font>元，已完成：<font color=\"info\">" + ready0 + "</font>单，进行中：<font color=\"info\">" + ing0 + "</font>单，未支付取消：<font color=\"info\">" + cancel0$10 + "</font>单，支付取消：<font color=\"info\">" + cancel0$20 + "</font>单，超时自动取消：<font color=\"info\">" + cancel0$30 +  "</font>单，加钟数：<font color=\"info\">" + extraTimePeoples0 + "</font>单。";
            sb.append(msg0);
            sb.append("\n");
        });
        MarkdownParam markdown = MarkdownParam.builder()
                .msgtype("markdown")
                .markdown(Markdown.builder().content(sb.toString()).build())
                .build();
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.set("content-type", "application/json");
        String res = httpService.sendPost("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=e8a2d27f-5488-44ee-87f9-a27bd1744fcd", requestHeaders,mapper.writeValueAsString(markdown));
        log.info("{}", res);
    }

    /**
     * 给财务的报表
     */
    @Scheduled(cron = "0 0 9 * * ?")
    public void statisticsPerDay4Finance() throws Exception {
        LocalDate localDate = LocalDate.now().minusDays(1);
        String beginDate = localDate + " 00:00:00";
        String endDate = localDate + " 23:59:59";
        CitiesServedCondition citiesServedCondition = new CitiesServedCondition();
        citiesServedCondition.setStatus(10);
        List<CitiesServed> citiesServedList = citiesServedService.listInfo(citiesServedCondition);
        StringBuilder sb = new StringBuilder();
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setBeginDate(beginDate);
        condition.setEndDate(endDate);
        List<OrderInfo> orderInfoList = orderService.orderList(condition);
        int todayPrice = 0;
        int extTotalPrice = 0;
        if (CollUtil.isNotEmpty(orderInfoList)) {
            todayPrice = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getOrderPrice(), 0)).sum();
            extTotalPrice = orderInfoList.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getExtTotalPrice(), 0)).sum();
        }
        String msg = "# " + localDate + "（按下单时间统计）\n <font color=\"warning\">平台数据</font>---业绩：<font color=\"info\">" + (todayPrice + extTotalPrice)/100 + "</font>元";
        sb.append(msg);
        sb.append("\n");
        citiesServedList.forEach(citiesServed -> {
            List<Shop> shop = shopService.getShopByCitiesId(citiesServed.getId());
            OrderInfoCondition condition0 = new OrderInfoCondition();
            condition0.setBeginDate(beginDate);
            condition0.setEndDate(endDate);
            if (CollUtil.isNotEmpty(shop)) {
                condition0.setShopIds(shop.stream().map(Shop::getId).toList());
            }
            List<OrderInfo> orderInfoList0 = orderService.orderList(condition0);
            int todayPrice0 = 0;
            int extTotalPrice0 = 0;
            if (CollUtil.isNotEmpty(orderInfoList0)) {
                todayPrice0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getOrderPrice(), 0)).sum();
                extTotalPrice0 = orderInfoList0.stream().filter(orderInfo -> orderInfo.getStatus()==40 || orderInfo.getStatus()==30).mapToInt(info -> ObjectUtil.defaultIfNull(info.getExtTotalPrice(), 0)).sum();
            }
            String msg0 = " <font color=\"warning\">" + citiesServed.getCityName() + "</font>数据---业绩：<font color=\"info\">" + (todayPrice0 + extTotalPrice0)/100 + "</font>元";
            sb.append(msg0);
            sb.append("\n");
        });
        MarkdownParam markdown = MarkdownParam.builder()
                .msgtype("markdown")
                .markdown(Markdown.builder().content(sb.toString()).build())
                .build();
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.set("content-type", "application/json");
        String res = httpService.sendPost("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=83211374-fd8f-4a5b-9f04-eaae33b6d9a5", requestHeaders,mapper.writeValueAsString(markdown));
        log.info("{}", res);
    }

    @Data
    @Builder
    private static class MarkdownParam {
        private String msgtype;
        private Markdown markdown;
    }
    @Data
    @Builder
    private static class Markdown {
        private String content;
    }
    /**
     * 每分钟执行一次，查询过期时间在当前时间之前 待支付的订单进行处理
     * 待支付 订单 10分钟后自动取消
     *
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void cancel$10() {
        OrderInfoCondition orderInfoCondition = new OrderInfoCondition();
        orderInfoCondition.setStatus(OrderStatusEnum.$10.getCode());
        orderInfoCondition.setExpiryTime(new Date());
        List<OrderInfo> orderInfoList = orderService.orderList(orderInfoCondition);
        orderInfoList.forEach(orderInfo -> {
            orderInfo.setCancelReason("超时自动取消");
            orderInfo.setSubStatus(OrderSubStatusEnum.$10.getCode());
            orderInfo.setUpdateBy(0L);//xx-job 标识
            orderService.cancelOrder(orderInfo);
            if (ObjectUtils.isNotEmpty(orderInfo.getTicketId())) {
                //退还优惠券
                userTicketService.returnUserTicket(orderInfo.getTicketId());
            }
            //使用了团购券，需要更新状态
            List<Coupon> coupons = couponService.getCouponsByOrderId(orderInfo.getId());
            coupons.forEach(coupon -> couponService.returnCoupon(coupon.getId()));
            log.info("XXL-JOB, Cancel Order.{}", orderInfo.getOrderCode());
        });

    }

    /**
     * 每分钟执行一次，查询15分钟之前的 待接单的订单进行处理
     * 待接单 订单 15分钟后没人接单，通知用户取消接单，只通知一次
     *
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void notificationUser() {
        int minutes = envUtil.isTest() ? 1 : 15;
        LocalDateTime now = LocalDateTime.now().minusMinutes(minutes);
        OrderInfoCondition orderInfoCondition = new OrderInfoCondition();
        orderInfoCondition.setStatus(OrderStatusEnum.$20.getCode());
        orderInfoCondition.setPayTime(OrderUtils.toDate(now));
        List<OrderInfo> orderInfoList = orderService.orderList(orderInfoCondition);
        orderInfoList.forEach(orderInfo -> {
            Boolean flag = redisUtil.hasKey(RedisKeyUtil.getNotificationUserKey(orderInfo.getOrderCode()));
            //不存在发送通知
            if (ObjectUtil.isNull(flag) || !flag) {
                smsProvider.sendNotice(orderInfo.getUserPhone(), HuaXinUtil.template3());
                redisUtil.set(RedisKeyUtil.getNotificationUserKey(orderInfo.getOrderCode()), orderInfo.getOrderCode());
                log.info("XXL-JOB, Notification User Cancel Order.{}", orderInfo.getOrderCode());
            }
        });
    }

    /**
     * 每分钟执行一次，查询5分钟之前的 待接单的订单进行处理
     * 待接单 订单 5分钟后没人接单，进行自动转单
     *
     */
    @Scheduled(cron = "0 0/1 * * * ?")
    public void autoTransfer() {
        int minutes = envUtil.isTest() ? 1 : 5;
        LocalDateTime now = LocalDateTime.now().minusMinutes(minutes);
        OrderInfoCondition orderInfoCondition = new OrderInfoCondition();
        orderInfoCondition.setStatus(OrderStatusEnum.$20.getCode());
        orderInfoCondition.setUpdateTime(OrderUtils.toDate(now));
        List<OrderInfo> orderInfoList = orderService.orderList(orderInfoCondition);
        orderInfoList.forEach(orderInfo -> {
            if (redisUtil.isMember(RedisKeyUtil.getAutoTransferGlobalKey(), String.valueOf(orderInfo.getId()))) {
                return;
            }
            redisUtil.add(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderInfo.getId())), orderInfo.getMassagistId() + "");
            List<String> list = new ArrayList<>();
            List<ShopUser> shopUsers = shopUserService.queryListByshopId(orderInfo.getShopId());
            if (CollUtil.isNotEmpty(shopUsers)) {
                list.addAll(shopUsers.stream().map(ShopUser::getPhone).toList());
            }
            //直接转单给我符合要求的真实技师，如果一个技师也没有，最终还是转单给调度员
            Massagist massagist1 = memberOrderApplicationService.getRealMassagist(orderInfo.getShopId(), orderInfo.getAppointTime(), redisUtil.members(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderInfo.getId()))));
            log.info("Massagist:massagist1 {}", massagist1);
            if (ObjectUtil.isNull(massagist1)) {
                orderService.orderTransfer(orderInfo, 0L, "system");
                //语音通知
                if (smsConfig.isFlagSendVoiceSms()) {
                    List<String> whiteList = sysConfigurationRepositoryService.getValueByKey(KeyConstant.SMS_WHITELIST);
                    list.removeAll(whiteList);
                    list.forEach(phone -> {
                        LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                        templateArgs.put("var1", orderInfo.getOrderCode());
                        try {
                            voiceSenderService.send(phone, templateArgs, "402502");
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
                }
                redisUtil.add(RedisKeyUtil.getAutoTransferGlobalKey(), String.valueOf(orderInfo.getId()));
            } else {
                //转单到技师
                orderService.orderTransfer2Massagist(orderInfo, 0L, "system", massagist1);
                //语音通知
                if (smsConfig.isFlagSendVoiceSms()) {
                    LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                    templateArgs.put("var1", orderInfo.getOrderCode());
                    try {
                        voiceSenderService.send(massagist1.getPhone(), templateArgs, "402502");
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
    }

}
